﻿using Microsoft.AspNetCore.Mvc.ModelBinding;
using Newtonsoft.Json;
using StarsAdmin.Core.Extensions;

namespace StarsAdmin.Core.ModelBinder
{
    /// <summary>
    /// 自定义的查询集合模型绑定，标记在要绑定的属性上，[QuerySetInput<QueryCondition>]
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class QuerySetInputModelBinder<T> : IModelBinder
    {
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            // 空检查
            if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));

            // 获取模型名和类型
            var modelName = bindingContext.ModelName;
            var modelType = bindingContext.ModelType;

            // 获取 URL 参数集合
            var queryCollection = bindingContext.HttpContext.Request.Query;

            // 一、尝试从 status[] 获取值
            if (queryCollection.ContainsKey(modelName + "[]"))
            {
                var values = ConvertValues(queryCollection[modelName + "[]"], modelType);
                bindingContext.Result = ModelBindingResult.Success(values);

                return Task.CompletedTask;
            }

            // 二、处理fetch发送的数组默认格式
            var modelNameArr = queryCollection[modelName];
            if (!string.IsNullOrEmpty(modelNameArr))
            {
                List<T> values = new List<T>();
                foreach (var item in modelNameArr)
                {
                    var value = JsonConvert.DeserializeObject<T>(item);
                    values.Add(value);
                }
                if (values.Count > 0)
                {
                    bindingContext.Result = ModelBindingResult.Success(values);
                    return Task.CompletedTask;
                }
            }
            // 三、处理axios发送的数组默认格式，queryConditions[index][propName]
            if (queryCollection.Count > 0)
            {
                List<T> values = new List<T>();
                var props = typeof(T).GetProperties();
                int index = 0;
                while (index < queryCollection.Count)
                {
                    Dictionary<string, object> propDic = new Dictionary<string, object>();
                    foreach (var prop in props)
                    {
                        var propName = prop.Name.FirstCharToLower();
                        var propValue = queryCollection[$"{modelName.FirstCharToLower()}[{index}][{propName}]"].FirstOrDefault();
                        if (propValue != null)
                        {
                            var convertedValue = ConvertValue(propValue, prop.PropertyType);
                            propDic.Add(propName, convertedValue);
                        }
                    }
                    if (propDic.Count > 0)
                    {
                        // 将字典先转换成json，然后在转换成 T
                        var value = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(propDic));
                        values.Add(value);
                    }
                    index += 1;
                }
                if (values.Count > 0)
                {
                    bindingContext.Result = ModelBindingResult.Success(values);
                    return Task.CompletedTask;
                }
            }

            return Task.CompletedTask;
        }

        /// <summary>
        /// 转换集合类型值为模型类型值
        /// </summary>
        /// <param name="values"></param>
        /// <param name="modelType"></param>
        /// <returns></returns>
        private static object ConvertValues(IEnumerable<string> values, Type modelType)
        {
            // 处理数组类型
            if (modelType.IsArray)
            {
                return values.Select(u => u.ChangeType<T>()).ToArray();
            }
            // 处理 List 类型
            if (modelType.IsGenericType && modelType.GetGenericTypeDefinition() == typeof(List<>))
            {
                return values.Select(u => u.ChangeType<T>()).ToList();
            }

            // IEnumerable<T> 类型
            return values.Select(u => u.ChangeType<T>());
        }

        private object ConvertValue(string value, Type targetType)
        {
            if (targetType == null || targetType == typeof(object))
            {
                return value;
            }

            var typeConverter = System.ComponentModel.TypeDescriptor.GetConverter(targetType);
            if (typeConverter != null && typeConverter.CanConvertFrom(typeof(string)))
            {
                return typeConverter.ConvertFrom(value);
            }

            return value;
        }
    }
}